package Montecarlo;

import java.util.ArrayList;
import java.util.Random;

public class Montecarlo {
	private ArrayList<HiloMontecarlo> procesos;
	private double minFunc;
	private double[] argsFunc;

	public Montecarlo(Funcion funcObj, int numIter) {
		procesos = new ArrayList<HiloMontecarlo>();

		// obtengo el numero de hilos de acuerdo al numero de nucleos del procesador
		int numHilos = Runtime.getRuntime().availableProcessors();

		// creo tantos hilos como numHilos indique
		for (int i = 0; i < numHilos; i++)
			procesos.add(new HiloMontecarlo(funcObj, numIter / numHilos));
	}

	/**
	 * Realiza la accion de optimizar la funcion
	 */
	public void optimizar() {
		// mando a ejecutar a los procesos
		for (HiloMontecarlo proc : procesos)
			proc.start();

		// espero que cada proceso termine
		for (HiloMontecarlo proc : procesos)
			try {
				proc.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

		// se obtiene el minimo de la funcion
		minFunc = Double.MAX_VALUE;
		for (int i = 0; i < procesos.size(); i++)
			if (procesos.get(i).min < minFunc) {
				minFunc = procesos.get(i).min;
				argsFunc = procesos.get(i).params;
			}
	}

	/**
	 * @return El valor minimo de la funcion
	 */
	public double getMinFun() {
		return minFunc;
	}

	/**
	 * @return El vector de argumentos de la funcion
	 */
	public double[] getArgs() {
		return argsFunc;
	}

	/**
	 * Clase Thread que va a ejecutar el metodo de montecarlo
	 */
	private class HiloMontecarlo extends Thread {
		private Funcion funcObj;
		private int numIter;
		private double[] params;
		private double min;

		public HiloMontecarlo(Funcion funcObj, int numIter) {
			this.funcObj = funcObj;
			this.numIter = numIter;
			min = Double.MAX_VALUE;

			params = new double[funcObj.getNumParams()];
		}

		@Override
		public void run() {
			double[] paramsPrueba = new double[funcObj.getNumParams()];
			Random rand = new Random();

			for (int i = 0; i < numIter; i++) {
				for (int x = 0; x < paramsPrueba.length; x++)
					paramsPrueba[x] = rand.nextDouble() * 10;

				// evaluo la funcion
				double valFunc = funcObj.calcularFunc(paramsPrueba);

				// verifico el minimo
				if (valFunc < min) {
					min = valFunc;
					System.arraycopy(paramsPrueba, 0, params, 0, params.length);
				}
			}
		}
	}
}
